package treinadores;

import java.util.ArrayList;
import java.util.Arrays;

import org.neuroph.core.learning.SupervisedTrainingElement;
import org.neuroph.core.learning.TrainingSet;
import org.neuroph.nnet.MultiLayerPerceptron;
import org.neuroph.nnet.learning.DynamicBackPropagation;

import interfaces.ITreinadorRN;

public class TreinadorAvanco implements ITreinadorRN {

	/**
	 * Número de entradas para a rede neural
	 */
	int numeroDeEntradas = 5;
	/**
	 * Número de saídas para a rede neural São 4 saídas, que implicam qual campo
	 * de um setor o jogador vai se mover
	 */
	int numeroDeSaidas = 4;
	/**
	 * Arquivo correspondente a rede neural
	 */
	String MLP_FILE = "avanco.nnet";
	/**
	 * Nome do arquivo de testes
	 */
	String TS_FILE = "avanco.set";
	/**
	 * Conjunto de treinamento com 5 campos de entrada (vide problema) e 2
	 * campos de saida
	 * 
	 * Times de 3 jogadores e um refém cada implicam em 8 entradas
	 * 
	 */
	TrainingSet training = new TrainingSet(numeroDeEntradas, numeroDeSaidas);
	/**
	 * Classe necessária para realizar o backpropagation
	 */
	DynamicBackPropagation learning;
	/**
	 * MultilayerPerceptron ou rede neural
	 */
	MultiLayerPerceptron mlp;

	/**
	 * Cria uma rede neural para testes O conteúdo dela é salvo em um arquivo
	 */
	public TreinadorAvanco() {
		// necessário para realizar o backPropagation
		learning = new DynamicBackPropagation();
		learning.setMaxIterations(500);
		learning.setLearningRate(0.7);
		// Número de camadas
		ArrayList<Integer> camadas = new ArrayList<Integer>();
		// camada 1 : 2 neuronios
		camadas.add(numeroDeEntradas);
		// Camada 2 : 12 neuronios
		camadas.add(12);
		// Camada 3 : 2 neuronios
		camadas.add(numeroDeSaidas);
		// Cria um perceptron
		mlp = new MultiLayerPerceptron(camadas);
		// adiciona as entradas de treinamento
		adicionaEntradasDeTreinamento();
	}

	/**
	 * Adiciona entradas para fazer o treinamento
	 */
	public void adicionaEntradasDeTreinamento(){
    	//aqui são adicionadas algumas entradas aleatórias e calculadas algumas saídas
    	//Algoritmo preliminar de treinamento, não sei se vai ficar satisfatório
    	//o bastante.
    	//A RN funciona para cada jogador
    	//O treinamento tenta elimiar redundâncias de posições iguais de jogadores
    	
    	for(int posicaoJogadorNossoTime = 0; posicaoJogadorNossoTime <= 15; posicaoJogadorNossoTime++){
	        for(int posicaoJogadorAdversario1 = 0; posicaoJogadorAdversario1 <= 15; posicaoJogadorAdversario1++){
	        	for(int posicaoJogadorAdversario2 = 1; posicaoJogadorAdversario2 <= 15; posicaoJogadorAdversario2 += 4)
	        	{
	        		for(int posicaoJogadorAdversario3 = 2; posicaoJogadorAdversario3 <= 15; posicaoJogadorAdversario3 += 4)
	            	{
	        			for(int posicaoJogadorAdversario4 = 3; posicaoJogadorAdversario4 <= 15; posicaoJogadorAdversario4 += 4)
	                	{
	        			    
//	                    	  double adiciona = Math.random();
//	                    	  
//	                    	  if(adiciona < 0.1)
//	                    	  {
	                    		  int [] jogadores = {posicaoJogadorNossoTime,
	                    				  posicaoJogadorAdversario1,posicaoJogadorAdversario2,
	                    				  posicaoJogadorAdversario3
	                    				  ,posicaoJogadorAdversario4};
	                    		  
	                    		  double [] saidaDesejada = CalculosAuxiliares.getSaidaDesejada(posicaoJogadorNossoTime, 
	                    				  Arrays.copyOfRange(jogadores, 1, jogadores.length));
	                    		  training.addElement(new SupervisedTrainingElement(
	                    				  //entrada
	                    				  new double[] {posicaoJogadorNossoTime,
	                    				  posicaoJogadorAdversario1,posicaoJogadorAdversario2,posicaoJogadorAdversario3,
	                    				  posicaoJogadorAdversario4},
	                    				  //saida
	                    				  saidaDesejada));
//	                    	  }
	                	}
	            	}
	        	}
	    	}
    	}
    }

	/**
	 * Treina a rede e salva no arquivo do conjunto de treinamento
	 */
	public void treinarRede() {
		// Treina a rede!
		mlp.learnInSameThread(training, learning);
	}

	@Override
	public void treinar() {
		// treina a rede
		treinarRede();
		// salvando arquivos
		mlp.save(MLP_FILE);
		training.save(TS_FILE);
	}
}